Code:
#include <stdio.h>
#include <time.h>
static const struct tm zero = {0};
static struct tm worker = {0};
struct tm *nth_weekday_of_month(int week, int wday, int month, int year)
{
struct tm *result = &worker;
time_t t;
*result = zero;
/*
* Build the first day of the month.
*/
worker.tm_year = year;
worker.tm_mon = month;
worker.tm_mday = 1;
/*
* Create the calendar time. Then normalize it back to broken-down form.
*/
t = mktime(result);
if ( t == (time_t)-1 )
{
return 0;
}
/*
* Go to Nth week, where N is an offset from the first week.
* N = 1 is the 2nd week
* N = 0 is the 1st week
* N = -1 is the last week of the previous month
*/
if ( wday != result->tm_wday )
{
result->tm_mday += wday - result->tm_wday + 7 * (wday < result->tm_wday);
}
result->tm_mday += 7 * week;
/*
* Re-normalize it.
*/
t = mktime(result);
if ( t == (time_t)-1 )
{
return 0;
}
return result;
}
void test_last_sunday_of_march(void)
{
int year;
puts("Last Sunday of March over a number of years.");
for ( year = 100; year < 112; ++year )
{
/* Last Sunday of March is the -1th Sunday of April. */
struct tm *bst = nth_weekday_of_month(-1, 0/*Sunday*/, 3/*April*/, year);
if ( bst )
{
char *text = asctime(bst);
text[24] = '\0';
puts(text);
}
}
}
void test_last_wday_of_march(void)
{
int wday;
puts("Last Sunday, Monday, Tuesday, etc. of March in 2007.");
for ( wday = 0; wday < 7; ++wday )
{
struct tm *bst = nth_weekday_of_month(-1, wday, 3/*April*/, 107);
if ( bst )
{
char *text = asctime(bst);
text[24] = '\0';
puts(text);
}
}
}
void test_nth_week(void)
{
int wday, week;
puts("Nth Sunday of month (0=first):");
for ( week = -1; week < 6; ++week )
{
printf("week %d\n", week);
for ( wday = 0; wday < 7; ++wday )
{
struct tm *bst = nth_weekday_of_month(week, wday, 0, 107);
if ( bst )
{
char *text = asctime(bst);
text[24] = '\0';
puts(text);
}
}
}
}
void holidays(void)
{
char text[BUFSIZ];
int year;
puts("Variable-Date Holidays:");
for ( year = 100; year < 112; ++year )
{
struct tm *dst;
/* MLK */
dst = nth_weekday_of_month(2, 1, 0, year);
if ( dst )
{
strftime(text, sizeof text, "%a %b %d %Y", dst);
printf("%s Martin Luther King holiday\n", text);
}
/* Washington's birthday observed */
dst = nth_weekday_of_month(2, 1, 1, year);
if ( dst )
{
strftime(text, sizeof text, "%a %b %d %Y", dst);
printf("%s Washington's birthday observed\n", text);
}
/* Daylight Saving time begins */
dst = nth_weekday_of_month(-1, 0, 3, year);
if ( dst )
{
strftime(text, sizeof text, "%a %b %d %Y", dst);
printf("%s Daylight Saving time begins\n", text);
}
/* Armed Forces Day */
dst = nth_weekday_of_month(2, 6, 4, year);
if ( dst )
{
strftime(text, sizeof text, "%a %b %d %Y", dst);
printf("%s Armed Forces Day\n", text);
}
/* Memorial Day */
dst = nth_weekday_of_month(-1, 1, 5, year);
if ( dst )
{
strftime(text, sizeof text, "%a %b %d %Y", dst);
printf("%s Memorial Day\n", text);
}
/* Labor Day */
dst = nth_weekday_of_month(0, 1, 8, year);
if ( dst )
{
strftime(text, sizeof text, "%a %b %d %Y", dst);
printf("%s Labor Day\n", text);
}
/* Columbus Day */
dst = nth_weekday_of_month(1, 1, 9, year);
if ( dst )
{
strftime(text, sizeof text, "%a %b %d %Y", dst);
printf("%s Columbus Day\n", text);
}
/* Daylight Saving time ends */
dst = nth_weekday_of_month(-1, 0, 10, year);
if ( dst )
{
strftime(text, sizeof text, "%a %b %d %Y", dst);
printf("%s Daylight Saving time ends\n", text);
}
/* Thanksgiving Day */
dst = nth_weekday_of_month(3, 4, 10, year);
if ( dst )
{
strftime(text, sizeof text, "%a %b %d %Y", dst);
printf("%s Thanksgiving Day\n", text);
}
putchar('\n');
}
}
int main(void)
{
test_last_sunday_of_march();
test_last_wday_of_march();
test_nth_week();
holidays();
return 0;
}
To test other areas, you may note that I played around with some variable-date US holidays. I haven't really checked them, other than to make sure Labor Day and Memorial Day were on a Monday, or that Thanksgiving was on a Thursday.